﻿using System;
using System.Collections.Generic;

namespace ChessKit.Common
{
  public struct BoardLocation : IEquatable<BoardLocation>
  {
    public int X { get; set; }
    public int Y { get; set; }
    public BoardArea Area { get; set; }

    public Position Pos
    {
      get { return new Position(X - 1, Y - 1); }
      set
      {
        X = value.X + 1;
        Y = value.Y + 1;
        Area = BoardArea.Cell;
      }
    }
    private BoardLocation(int x, int y, BoardArea area)
      : this()
    {
      X = x;
      Y = y;
      Area = area;
    }

    public static IEnumerable<BoardLocation> Cells
    {
      get
      {
        for (var i = 1; i <= 8; i++)
          for (var j = 1; j <= 8; j++)
            yield return Cell(i, j);
      }
    }
    public static IEnumerable<BoardLocation> All
    {
      get
      {
        yield return Background;
        yield return TopLeftCorner;
        yield return TopRightCorner;
        yield return BottomLeftCorner;
        yield return BottomRightCorner;

        for (var i = 1; i <= 8; i++)
          for (var j = 1; j <= 8; j++)
            yield return Cell(i, j);

        for (var i = 0; i <= 8; i++)
        {
          yield return Row(i);
          yield return Column(i);
          yield return ShortRow(i);
          yield return ShortColumn(i);
        }
        for (var i = 1; i <= 8; i++)
        {
          yield return TopEdge(i);
          yield return LeftEdge(i);
          yield return RightEdge(i);
          yield return BottomEdge(i);
        }

      }
    }

    public static readonly BoardLocation Unknown = new BoardLocation(int.MinValue, int.MinValue, BoardArea.Unknown);
    public static readonly BoardLocation Background = new BoardLocation(-1, -1, BoardArea.Background);
    public static readonly BoardLocation BottomLeftCorner = new BoardLocation(0, 0, BoardArea.Background);
    public static readonly BoardLocation TopLeftCorner = new BoardLocation(0, 9, BoardArea.Background);
    public static readonly BoardLocation BottomRightCorner = new BoardLocation(9, 0, BoardArea.Background);
    public static readonly BoardLocation TopRightCorner = new BoardLocation(9, 9, BoardArea.Background);

    public static BoardLocation Cell(int column, int row)
    {
      if (column < 1 || column > 8) throw new ArgumentOutOfRangeException("column");
      if (row < 1 || row > 8) throw new ArgumentOutOfRangeException("row");
      return new BoardLocation(column, row, BoardArea.Cell);
    }
    public static BoardLocation Row(int row)
    {
      if (row < 0 || row > 8) throw new ArgumentOutOfRangeException("row");
      return new BoardLocation(-1, row, BoardArea.Row);
    }
    public static BoardLocation Column(int column)
    {
      if (column < 0 || column > 8) throw new ArgumentOutOfRangeException("column");
      return new BoardLocation(column, -1, BoardArea.Column);
    }
    public static BoardLocation ShortRow(int row)
    {
      if (row < 0 || row > 9) throw new ArgumentOutOfRangeException("row");
      return new BoardLocation(-1, row, BoardArea.ShortRow);
    }
    public static BoardLocation ShortColumn(int column)
    {
      if (column < 0 || column > 9) throw new ArgumentOutOfRangeException("column");
      return new BoardLocation(column, -1, BoardArea.ShortColumn);
    }
    public static BoardLocation TopEdge(int column)
    {
      if (column < 1 || column > 8) throw new ArgumentOutOfRangeException("column");
      return new BoardLocation(column, 0, BoardArea.TopEdgeCell);
    }
    public static BoardLocation BottomEdge(int column)
    {
      if (column < 1 || column > 8) throw new ArgumentOutOfRangeException("column");
      return new BoardLocation(column, 9, BoardArea.BottomEdgeCell);
    }
    public static BoardLocation LeftEdge(int row)
    {
      if (row < 1 || row > 8) throw new ArgumentOutOfRangeException("row");
      return new BoardLocation(0, row, BoardArea.LeftEdgeCell);
    }
    public static BoardLocation RightEdge(int row)
    {
      if (row < 1 || row > 8) throw new ArgumentOutOfRangeException("row");
      return new BoardLocation(9, row, BoardArea.RightEdgeCell);
    }

    #region ' Equality '

    public bool Equals(BoardLocation other)
    {
      return other.X == X && other.Y == Y && Equals(other.Area, Area);
    }

    public override bool Equals(object obj)
    {
      if (ReferenceEquals(null, obj)) return false;
      if (obj.GetType() != typeof(BoardLocation)) return false;
      return Equals((BoardLocation)obj);
    }

    public override int GetHashCode()
    {
      unchecked
      {
        int result = X;
        result = (result * 397) ^ Y;
        result = (result * 397) ^ Area.GetHashCode();
        return result;
      }
    }

    public static bool operator ==(BoardLocation left, BoardLocation right)
    {
      return left.Equals(right);
    }

    public static bool operator !=(BoardLocation left, BoardLocation right)
    {
      return !left.Equals(right);
    }

    #endregion

    public override string ToString()
    {
      return Area + ": " + X + ", " + Y;
    }
  }
}